上下文分层
上下文分层的第一目标是提升信息密度,其次才是解决上下文窗口有限的问题
临时性的信息和长期性信息混在一起会使得模型看到的内容过多,导致模型无法聚焦在重要的信息上
flowchart TB
subgraph Outside[❌ 永不进入层]
A1[Hooks 逻辑]
A2[明确规则]
A3[工具约束]
end
subgraph Context[✅ 模型上下文]
direction TB
subgraph Layer1[常驻层 - 每次会话必载]
B1[身份定义]
B2[项目约定]
B3[安全围栏]
end
subgraph Layer2[按需加载层 - 需要时载入]
C1[Skills]
C2[领域知识]
C3[使用说明]
end
subgraph Layer3[运行时注入层 - 动态注入]
D1[时间/日期]
D2[通道 ID]
D3[用户偏好]
end
end
subgraph Memory[💾 外部存储]
E1[MEMORY.md]
E2[跨会话记忆]
end
Outside -.->|不进入 | Context
Memory -.->|按需读取 | Context
style Outside fill:#ffe4e1,stroke:#333,stroke-dasharray:5 5
style Context fill:#e8f5e9,stroke:#333
style Memory fill:#fff3e0,stroke:#333
style Layer1 fill:#c8e6c9
style Layer2 fill:#dcedc8
style Layer3 fill:#f0f4c3
五层说明:
| 层级 | 内容 | 加载时机 | 示例 |
|---|---|---|---|
| 常驻层 | 身份定义、项目约定、安全围栏 | 每次会话 | ”你是资深前端工程师”、“使用 TypeScript” |
| 按需加载层 | Skills、领域知识、使用说明 | 需要时才加载 | 调用数据库技能时加载 DB 相关文档 |
| 运行时注入层 | 时间、通道 id、用户偏好 | 每次请求动态注入 | 当前时间、用户 ID、主题偏好 |
| 记忆层 | MEMORY.md 跨会话累计的知识 | 需要时读取 | 用户工作风格、项目历史决策 |
| 永不进入层 | Hooks 逻辑、明确的规则 | 永不进入 | 通过工具而非 prompt 约束 |
对话流工程
对话流工程的核心目标是在有限的上下文窗口内,实现对话状态的可持续追踪和历史信息的按需访问。通过节点化的消息管理和分层汇总机制,让长程对话也能保持上下文的一致性。
核心概念
1. 消息节点(Message Node)
对话中的每条消息都是一个独立的节点,包含:
- 消息内容(用户输入或模型输出)
- 时间戳和序列号
- 与前序节点的连接关系
- 节点类型标记(普通消息/汇总节点/分支节点)
2. 汇总节点(Summary Node)
汇总节点是对话流中的关键锚点,承担三个职责:
- 状态压缩:将之前的对话内容提炼为关键信息
- 持久化存储:将摘要写入硬盘(如 MEMORY.md),跨会话保留
- 指针标记:通过
last_summary_index记录最后处理位置
3. 上下文窗口管理
┌─────────────────────────────────────────────────────────────┐
│ 完整对话历史 (硬盘) │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ M1 │─│ M2 │─│ S1 │─│ M3 │─│ M4 │─│ S2 │─│ M5 │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ ↑ ↑ │
│ last_summary_index 当前 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 活动上下文窗口 (内存/Token) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ S2 │─│ M5 │─│ ... │ │
│ └─────┘ └─────┘ └─────┘ │
│ ↑ ↑ │
│ 锚点 最新 │
└─────────────────────────────────────────────────────────────┘
对话流程
flowchart TD
A([新消息]) --> B[创建 message 节点]
B --> C{是否需要压缩?}
C -->|超出 autocompact 阈值 | D[压缩工作流]
C -->|否 | E[根据 last_summary_index<br/>获取历史消息]
subgraph D [压缩工作流]
D1[分级压缩<br/>信息权重评估] --> D2[历史信息处理<br/>关键信息保留]
D2 --> D3[压缩策略选择<br/>摘要/删除/合并]
D3 --> D4[更新 last_summary_index]
end
D4 --> E
E --> F[构建上下文]
F --> G[LLM 对话]
G --> H[响应 message 节点]
H --> I([输出])
style D fill:#ffe4e1,stroke:#333,stroke-dasharray:5 5
style D1 fill:#f9f,stroke:#333
style D2 fill:#f9f,stroke:#333
style D3 fill:#f9f,stroke:#333
style D4 fill:#bbf,stroke:#333
style E fill:#bbf,stroke:#333
style F fill:#bbf,stroke:#333
style G fill:#98FB98,stroke:#333
style H fill:#ffd,stroke:#333
流程说明:
- 新消息接入:用户输入被包装为 message 节点,进入处理管道
- 压缩判断:检查当前上下文是否超出
autocompact阈值- 阈值可基于 token 数、消息条数或时间窗口设定
- 压缩工作流(复杂模块):
- 分级压缩:根据信息权重评估,决定哪些内容需要保留、压缩或删除
- 历史信息处理:提取关键信息,识别长期有用的上下文
- 压缩策略选择:动态选择摘要生成、内容删除或相似内容合并
- 完成后更新
last_summary_index指针
- 上下文构建:从
last_summary_index位置开始拉取历史消息,组装完整上下文 - LLM 对话:发送上下文至模型,获取响应
- 响应输出:将模型响应包装为 message 节点,返回给用户
上下文加载策略
对话流的上下文采用分层加载策略,不同层次的信息在不同时机进入上下文:
sequenceDiagram
participant User as 用户
participant CM as 上下文管理器
participant FS as 文件系统
participant Model as LLM
User->>CM: 发送新消息
CM->>CM: 创建消息节点
rect rgb(240, 248, 255)
Note right of CM: 上下文组装阶段
CM->>FS: 读取 last_summary_index
FS-->>CM: 返回指针位置
CM->>FS: 拉取指针后的消息节点
FS-->>CM: 返回历史消息
CM->>FS: 读取常驻层内容
FS-->>CM: 身份定义/项目约定
CM->>FS: 按需加载 Skills
FS-->>CM: 返回相关技能文档
end
CM->>CM: 组装完整上下文
CM->>Model: 发送上下文 + 新消息
Model-->>CM: 返回响应
CM-->>User: 输出结果
rect rgb(255, 250, 240)
Note right of CM: 可选:触发汇总
CM->>CM: 判断是否需要汇总
CM->>CM: 生成汇总节点
CM->>FS: 持久化摘要
CM->>FS: 更新 last_summary_index
end
回退与分支机制
对话流支持回退到任意历史节点并开启新的分支:
flowchart LR
S1((S1)) --> M3[M3]
M3 --> M4[M4]
M4 --> S2((S2))
S2 --> M5[M5]
S2 --> M5'[M5' 分支]
M5' --> M6'[M6']
M5 -.->|回退点 | M4
M4 --> M4a[M4a 新路径]
M4a --> M5a[M5a]
style S1 fill:#f9f
style S2 fill:#f9f
style M5' fill:#ffd
style M6' fill:#ffd
style M4a fill:#dfd
style M5a fill:#dfd
实现要点
- 指针管理:
last_summary_index必须原子更新,避免指针与摘要内容不一致 - 节点 ID 生成:使用时间戳 + 哈希确保唯一性,支持快速定位
- 软连接实现:通过引用节点 ID 而非复制内容来保持上下文精简
- 汇总触发条件:可基于 token 数、消息数量、时间间隔等维度决策